package org.elsfs.cloud.common.mybatis.ext.sql;

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import org.elsfs.cloud.common.core.entity.BaseEntity;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.FieldEnumResolverSupport;
import org.elsfs.cloud.common.mybatis.ext.sql.support.update.CompositeSetItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.update.ExpressionSetItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.update.SetItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.update.StandardSetItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.update.StandardUpdateItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.update.UpdateItem;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

/**
 * 抽象更新SQL实现
 *
 * @param <C> 子类具体类型
 */
@SuppressWarnings("unchecked")
public abstract class AbstractUpdateSql<C extends AbstractUpdateSql<C>> extends AbstractJoin<C> implements UpdateSql<C, AbstractUpdateSql<C>.UpdateConditionBuilderImpl> {

    /**
     * 具体子类实现引用
     */
    protected final C childThis = (C) this;
    /**
     * 更新表
     */
    private UpdateItem updateItem;
    /**
     * 更新设置项
     */
    private final Collection<SetItem> setItems = new ArrayList<>();
    /**
     * 更新条件构建器
     */
    private UpdateConditionBuilderImpl updateConditionBuilder;

    /**
     * 设置更新数据的表
     *
     * @param entityClass 实体类
     * @return 具体实现
     */
    @Override
    public C update(Class<? extends BaseEntity> entityClass) {
        this.updateItem = new StandardUpdateItem(
            TableInfoHelper.getTableInfo(entityClass)
                .getTableName(),
            null
        );

        return this.childThis;
    }

    /**
     * 设置更新数据的表
     *
     * @param table 表名
     * @return 具体实现
     */
    @Override
    public C update(String table) {
        this.updateItem = new StandardUpdateItem(
            table,
            null
        );

        return this.childThis;
    }

    /**
     * 设置更新数据的表
     *
     * @param entityClass 实体类
     * @param tableAlias  表别名
     * @return 具体实现
     */
    @Override
    public C update(Class<? extends BaseEntity> entityClass, String tableAlias) {
        this.updateItem = new StandardUpdateItem(
            TableInfoHelper.getTableInfo(entityClass)
                .getTableName(),
            tableAlias
        );

        return this.childThis;
    }

    /**
     * 设置更新数据的表
     *
     * @param table      表名
     * @param tableAlias 表别名
     * @return 具体实现
     */
    @Override
    public C update(String table, String tableAlias) {
        this.updateItem = new StandardUpdateItem(
            table,
            tableAlias
        );

        return this.childThis;
    }

    /**
     * 设置字段表达式
     *
     * @param field      字段名称
     * @param expression 表达式
     * @param args       表达式参数
     * @return 具体实现
     */
    @Override
    public C set(String field, String expression, Object... args) {
        this.setItems.add(
            new ExpressionSetItem(field, expression, args)
        );

        return this.childThis;
    }

    /**
     * 设置字段表达式
     *
     * @param field      字段枚举
     * @param expression 表达式
     * @param args       表达式参数
     * @return 具体实现
     */
    @Override
    public C set(Enum<?> field, String expression, Object... args) {
        return this.set(
            FieldEnumResolverSupport.resolveFieldName(
                this.getTableAliasManager(),
                field
            ),
            expression,
            args
        );
    }

    /**
     * 设置字段值
     *
     * @param field 字段名称
     * @param value 值
     * @return 具体实现
     */
    @Override
    public C setValue(String field, Object value) {
        this.setItems.add(
            new StandardSetItem(this.getSqlParameterManager(), field, value)
        );

        return this.childThis;
    }

    /**
     * 设置字段值
     *
     * @param field 字段枚举
     * @param value 值
     * @return 具体实现
     */
    @Override
    public C setValue(Enum<?> field, Object value) {
        return this.setValue(
            FieldEnumResolverSupport.resolveFieldName(
                this.getTableAliasManager(),
                field
            ),
            value
        );
    }

    /**
     * 批量设置字段值
     *
     * @param fieldValueMap 字段值Map
     * @return 具体实现
     */
    @Override
    public C setValues(Map<String, Object> fieldValueMap) {
        for (Map.Entry<String, Object> entry : fieldValueMap.entrySet()) {
            this.setValue(entry.getKey(), entry.getValue());
        }

        return this.childThis;
    }

    /**
     * 批量设置字段值
     *
     * @param fieldValueMap 字段枚举值Map
     * @return 具体实现
     */
    @Override
    public C setValuesEnum(Map<Enum<?>, Object> fieldValueMap) {
        for (Map.Entry<Enum<?>, Object> entry : fieldValueMap.entrySet()) {
            this.setValue(entry.getKey(), entry.getValue());
        }

        return this.childThis;
    }

    /**
     * 设置更新条件
     *
     * @return 更新条件构建器
     */
    @Override
    public UpdateConditionBuilder<UpdateConditionBuilderImpl, C> where() {
        if (this.updateConditionBuilder == null) {
            this.updateConditionBuilder = new UpdateConditionBuilderImpl();
        }

        return this.updateConditionBuilder;
    }

    /**
     * 构建SQL之前处理
     */
    @Override
    protected void beforeBuild() {
        this.addSqlFragment(
            new TokenSqlFragment("UPDATE")
        );
        if (this.updateItem == null) {
            throw new MybatisPlusException( "更新SQL的表必须指定");
        }
        this.addSqlFragment(this.updateItem);
        if (this.setItems.isEmpty()) {
            throw new MybatisPlusException("更新SQL必须设置更新项");
        }
        super.beforeBuild();
        this.addSqlFragment(
            new CompositeSetItem(this.setItems)
        );
        if (this.updateConditionBuilder != null) {
            this.addSqlFragment(this.updateConditionBuilder);
        }
    }

    /**
     * 更新条件构建器实现
     */
    public class UpdateConditionBuilderImpl extends AbstractSortableConditionBuilder<UpdateConditionBuilderImpl> implements UpdateConditionBuilder<UpdateConditionBuilderImpl, C> {

        /**
         * 返回上一级对象
         *
         * @return 上级对象
         */
        @Override
        public C end() {
            return AbstractUpdateSql.this.childThis;
        }

        /**
         * 获取SQL参数管理器
         *
         * @return SQL参数管理器
         */
        @Override
        public SqlParameterManager getSqlParameterManager() {
            return AbstractUpdateSql.this.getSqlParameterManager();
        }

        /**
         * 获取表别名管理器
         *
         * @return 表名别管理器
         */
        @Override
        public TableAliasManager getTableAliasManager() {
            return AbstractUpdateSql.this.getTableAliasManager();
        }
    }
}
